# -*- coding: utf-8 -*-
__author__      = "Damien Lebrun"
__date__        = "$23-Nov-2008 12:43:58$"
__revision__    = "$Id: common.py 372 2008-12-03 10:51:36Z dinoboff $"

import codecs
import fnmatch
import logging
import os
import re
import sys
import settings

class UtilsError(Exception): pass

def create_file(file_name, content=None, base_dir=None):
	"""
	Create a file and return its path.
	It will also create the missing directory on its path
	"""
	if base_dir:
		path = os.path.join(base_dir, file_name)
	else:
		path = file_name

	# create missing root folders
	root = os.path.split(path)[0]
	if not os.path.exists(root):
		os.makedirs(root)

	# add content
	f = open(path, 'wb')
	if content:
		f.write(content.encode(settings.FILE_CHARSET))
	f.close()
	return path

def fix_eol(file_path, eol=u'\n', re_eol=None):
	"""
	Fix end of line of a file.
	Replace mixed windows and linux style of eol by one
	(the Linxu one by default).

	Raise a BuildError when the file can't opened or read.
	"""
	if re_eol == None:
		if eol == '\r\n':
			re_eol = re.compile(u"\n", re.UNICODE)
		else:
			re_eol = re.compile(u"\r\n", re.UNICODE)

	try:
		data = codecs.open(file_path, mode='rb', encoding=settings.FILE_CHARSET).read()
	except IOError, e:
		raise UtilsError("IOError: could not open or read %s. %s." % (file_path,str(e),))

	if '\0' in data:
		logging.getLogger('vanillafriends.fix_eol').warn(
			'binary content in %s' % file_path)
		return

	newdata = re_eol.sub(eol, data)
	if data != newdata:
		logging.getLogger('vanillafriends.fix_eol').info(
			'eol fixed in %s' % file_path)
		f = open(file_path, mode='wb')
		f.write(newdata.encode(settings.FILE_CHARSET))
		f.close()

def get_yui_compressor():
	path_to_jar = None
	
	# yui compressor should be in the the vanillafriends egg
	for p in sys.path:
		test = os.path.join(p, 'JAR/yuicompressor.jar')
		if os.path.exists(test):
			path_to_jar = test
			break
	
	# try in the current path
	if path_to_jar is None:
		test = os.path.join(os.curdir, 'JAR/yuicompressor.jar')
		if os.path.exists(test):
			path_to_jar = test
	
	return path_to_jar

def logger_factory(name='vanillafriends',
				   level=logging.WARNING,
				   formatter=logging.Formatter("%(levelname)s: %(message)s"),
				   filter_name='vanillafriends'):
	log = logging.getLogger(name)
	log.setLevel(level)

	info_handler = logging.StreamHandler(strm=sys.stdout)
	info_handler.setFormatter(formatter)
	info_handler.addFilter(logger_filter_factory(0, 25, name=filter_name))

	error_handler = logging.StreamHandler(strm=sys.stderr)
	error_handler.setFormatter(formatter)
	error_handler.addFilter(logger_filter_factory(25, 60, name=filter_name))

	log.addHandler(info_handler)
	log.addHandler(error_handler)

	return log

def logger_filter_factory(min_level=0, max_level=60, name='vanillafriends'):
	class Filter(logging.Filter):
		def filter(self,record):
			return min_level < int(record.levelno) <= max_level
	return Filter(name=name)



def replace_tokens(file_path, tokens, delimiter='@@'):
	"""
	Replace tokens (like @@AUTHOR@@) in files.
	"file_path" is the path to the file, "tokens" a dictionary;
	its keys are the tokens to replace, its value the replecment.
	"delimiter" define the sequence of characters
	that delimit the token in the file.

	Raise a BuildError when the file can't opened or read.
	"""
	try:
		data = codecs.open(file_path, mode='rb', encoding=settings.FILE_CHARSET).read()
	except IOError, e:
		raise UtilsError("IOError: could not open or read %s. %s." % (file_path,str(e),))
	if '\0' in data:
		logging.getLogger('vanillafriends.replace_tokken').warn(
			'binary content in %s' % file_path)
		return

	newdata = data
	for token in tokens.keys():
		newdata = newdata.replace(delimiter+token+delimiter, tokens[token])

	if data != newdata:
		logging.getLogger('vanillafriends.replace_tokken').info(
			'Tokkens replaced in %s' % file_path)
		f = open(file_path, "wb")
		f.write(newdata.encode(settings.FILE_CHARSET))
		f.close()

class Command(object):
	"""
	Generric command.
	"""
	def __init__(self,
				 base_dir=os.curdir,
				 logger_level=logging.WARNING,
				 logger_factory=logger_factory,
				 logger_name='vanillafriends'):
		base_dir = os.path.normpath(base_dir)
		self.base_dir = os.path.abspath(base_dir)
		self.log = logger_factory(
			name=logger_name,
			level=logger_level)


	def _normalize_name(self, name):
		"""
		Used to capitalise the extension name and remove space.
		It make sure the extension name can be used for a folder name.
		"""
		name = u''.join([w.capitalize() for w in re.sub(u"[-_\\/]+", u' ', name).split()])
		return re.compile(u"[^\w\d\.]+", re.UNICODE).sub(u'', name)

class FileList(object):
	def __init__(self,
				 warn=None,
				 debug_print=False):

		self.log = logging.getLogger(
			name='vanillafriends.FileList')

		self.allfiles = None
		self.files = []

	def set_base_dir(self, dir):
		dir = os.path.abspath(dir)
		self.base_dir = os.path.normpath(dir)

	def findall (self, dir=os.curdir):
		self.allfiles = []
		self.set_base_dir(dir)
		for path,dirs,files in os.walk(self.base_dir):
			for file_name in files:
				self.allfiles.append(os.path.join(path, file_name))

	def include_pattern (self, pattern, anchor=None,
						 prefix=None, is_regex=None):
		
		if self.allfiles is None:
			self.findall()
		
		re_pattern = self.__get_pattern(pattern, prefix)
		self.log.debug('Including files with the following pattern: %s', re_pattern.pattern)
		
		found = False
		for file_path in self.allfiles:
			if re_pattern.search(file_path):
				found = True
				if not file_path in self.files:
					self.log.debug('adding %s', file_path)
					self.files.append(file_path)
		
		return found
	
	def exclude_pattern (self, pattern,
						 anchor=1, prefix=None, is_regex=0):
		
		re_pattern = self.__get_pattern(pattern, prefix)
		self.log.debug('Removing file with the following pattern: %s', re_pattern.pattern)
		
		found = False
		for file_path in self.files[0:]:
			if re_pattern.search(file_path):
				found = True
				self.log.debug('removing %s', file_path)
				self.files.remove(file_path)
		
		return found

	def __get_pattern(self, pattern, prefix):
		re_pattern = fnmatch.translate(pattern)
		
		if prefix:
			if prefix in ('./', '.',):
				prefix = self.base_dir 
			elif prefix.startswith('./'):
				prefix = os.path.join(self.base_dir, prefix[2:])
			
			prefix = os.path.normpath(prefix) + os.sep
			prefix = fnmatch.translate(prefix)
			re_pattern = prefix[:-1] + re_pattern
		
		return re.compile(re_pattern, re.UNICODE)